import { isObject, isArray, isSymbol, hasOwn } from '../utils'
import { createDepsMap, track, trigger, ITERATE_KEY, DepsMap } from './effect'
import { isRef } from './ref'

interface Reactive { // reactive 对象
	__v_isReactive?: true,
	__v_reactive?: Reactive, // 代理后的对象
	__v_raw?: any // 代理前的对象
	__v_depsMap?: DepsMap
}

/** 
 * 代理对象是Arr时，为includes、indexOf、lastIndexOf添加track监听
 */
const arrayInstrumentations: Record<string, Function> = {}
	;['includes', 'indexOf', 'lastIndexOf'].forEach(key => {
		arrayInstrumentations[key] = function (...args: any[]): any {
			// this：observed（代理后的对象）
			// 返回observed.__v_raw（被代理的对象target）
			const arr = toRaw(this) as any
			for (let i = 0, l = (this as any).length; i < l; i++) {
				track(arr, i + '')
			}
			// 方法执行获得的值
			const res = arr[key](...args)
			if (res === -1 || res === false) {
				// 判断是否有代理对象
				return arr[key](...args.map(toRaw))
			} else {
				return res
			}
		}
	})
export const isReactive = (v: any): v is Reactive =>
	v ? v.__v_isReactive === true : false

const isObservableType = [Object, Array, Map, Set, WeakMap, WeakSet]
const canObserve = (value: unknown): boolean =>
	isObservableType.some(type => value instanceof type)

/** 
 * 生成Reacive对象（使用Proxy实现）
 * @param target {Object} 需要监听的对象
 * @return Reactive对象
 */
export function reactive(target: object): Reactive
export function reactive(target: Reactive): Reactive {
	if (!isObject(target)) { // 如果不是对象，返回错误（不能代理）
		console.warn(`value cannot be made reactive: ${String(target)}`)
		return target
	}

	// 如果已经被代理，则直接返回
	if (target.__v_raw) return target
	if (target.__v_reactive) return target.__v_reactive

	// 不可操作的target
	if (!canObserve(target)) return target

	let handler = reactiveHandler();
	const observed = new Proxy(target, handler)

	Object.defineProperty(target, '__v_reactive', {
		configurable: true,
		value: observed
	})
	return observed;
}

export function reactive2(target: object): Reactive
export function reactive2(target) {
	if (!isObject(target)) { // 如果不是对象，返回错误（不能代理）
		console.warn(`value cannot be made reactive: ${String(target)}`)
		return target
	}

	// 如果已经被代理，则直接返回
	if (target.__v_raw) return target
	if (target.__v_reactive) return target.__v_reactive

	// 不可操作的target
	if (!canObserve(target)) return target

	let handler = Object.create(null);
	let depsMap = createDepsMap() // 依赖，存储在闭包中
	const observed = Object.create({
		__v_raw: target,
		__v_isReactive: true,
		__v_depsMap: depsMap
	});
	Object.keys(target).forEach((key) => {
		let item = target[key]
		handler[key] = {
			configurable: true,
			enumerable: true,
			get() {
				track(depsMap, key);
				const targetIsArray = isArray(target) // 判断代理的是否是数组
				let res = target[key]
				return isRef(res) ?
					// 值是ref
					targetIsArray ? res : res.value :
					// 代理子对象
					isObject(res) ? reactive2(res) : res;
			},
			set(value) {
				target[key] = value
				trigger(depsMap, key, !item)
				return true
			}
		}
	})
	Object.defineProperties(observed, handler)

	Object.defineProperty(target, '__v_reactive', {
		configurable: true,
		value: observed
	})
	return observed;
}
export function $set(observed: Reactive, key: string, value: any) {
	let depsMap = observed.__v_depsMap
	let target = observed.__v_raw
	Object.defineProperty(observed, key, {
		configurable: true,
		enumerable: true,
		get() {
			track(depsMap, key);
			const targetIsArray = isArray(target) // 判断代理的是否是数组
			let res = target[key]
			return isRef(res) ?
				// 值是ref
				targetIsArray ? res : res.value :
				// 代理子对象
				isObject(res) ? reactive2(res) : res;
		},
		set(new_value) {
			target[key] = new_value
			trigger(depsMap, key, !!target[key])
			return true
		}
	})
	observed[key] = toRaw(value)
}
export function $delete(observed: Reactive, key: string): boolean {
	let depsMap = observed.__v_depsMap
	let target = observed.__v_raw
	// 删除属性
	const result = Reflect.deleteProperty(target, key)
	if (result && !hasOwn(target, key)) { // 删除成功后出发trigger
		// trigger(depsMap, key)
		trigger(depsMap, ITERATE_KEY)
	}
	return result
}



/** 
 * 生成Proxy的Handler
 * @return {ProxyHandler} ProxyHandler对象
 */
function reactiveHandler(): ProxyHandler<object> {
	let depsMap = createDepsMap() // 依赖，存储在闭包中
	return {
		get: function (target, key, receiver) {
			if (key === '__v_raw') return target
			if (key === '__v_isReactive') return true

			const targetIsArray = isArray(target) // 判断代理的是否是数组
			if (targetIsArray && hasOwn(arrayInstrumentations, key)) {
				// 判断key值是否是'includes', 'indexOf', 'lastIndexOf' ,直接返回值（不做代理处理）
				return Reflect.get(arrayInstrumentations, key, receiver)
			}
			// res为获取值（res = target[key]）
			const res = Reflect.get(target, key, receiver)

			// 获取原型或Symbol直接返回值
			if (isSymbol(key) || key === '__proto__') return res

			track(depsMap, key);
			return isRef(res) ?
				// 值是ref
				targetIsArray ? res : res.value :
				// 代理子对象
				isObject(res) ? reactive(res) : res;
		},
		set: function (target, key, value, receiver) {
			const oldValue = target[key]
			value = toRaw(value) // 获取被代理前的值（如果value是reactive的话）
			// oldValue为ref，修改值不是ref，则替换ref的value
			if (!isArray(target) && isRef(oldValue) && !isRef(value)) {
				oldValue.value = value
				return true
			}

			const result = Reflect.set(target, key, value, receiver) // 设置值，返回是否成功
			trigger(depsMap, key, !oldValue)
			return result
		},
		deleteProperty: function (target, key) {
			// 删除属性
			const result = Reflect.deleteProperty(target, key)
			if (result && !hasOwn(target, key)) { // 删除成功后出发trigger
				// trigger(depsMap, key)
				trigger(depsMap, ITERATE_KEY)
			}
			return result
		},
		has: function (target, key) {
			const result = Reflect.has(target, key)
			track(depsMap, key);
			return result
		},
		ownKeys: function (target) {
			track(depsMap, ITERATE_KEY)
			return Reflect.ownKeys(target)
		}
	}
}


/** 
 * 返回reactive的原对象（被代理前的对象）
 */
export function toRaw<T>(observed: T): T {
	return (observed && toRaw((observed as Reactive).__v_raw)) || observed
}